import { type ComponentOptions, computed, defineComponent, Fragment, nextTick, type PropType, shallowRef, type VNode, watch } from "vue";
import type { RowClickEventArgs, VTableColumnRenderArgs, VTableColumnsType, VTableColumnType } from "./v-table-types";
import type { RawSlots } from "vue-virt-list/lib/utils";
import { useRender } from "@/framework/vue/hooks";
import { VirtList, type VirtListReturn } from "vue-virt-list";
import { camelize } from "@/framework/util/strings";
import "./v-table.less";
import { Empty, Spin } from "ant-design-vue";
import { noop } from "@/framework/util/warning.ts";

function getColumnItem(child: VNode): VTableColumnType {
  const props = child.props || {};
  for (const [k, v] of Object.entries(props)) {
    props[camelize(k)] = v;
  }
  return {
    key: child.key,
    ...props,
    render: (child.children as RawSlots)?.default as any,
  };
}

/** 虚拟滚动表格 */
export default defineComponent({
  name: "v-table",
  props: {
    rowKey: { type: String, default: "id" },
    rowHeight: { type: Number, default: 35 },
    columns: { type: Array as PropType<VTableColumnsType> },
    dataSource: { type: Array as PropType<any[]> },
    loading: { type: Boolean },
    deepWatch: { type: [Boolean, Number] as PropType<boolean | number>, default: false },
  },
  emits: {
    "row-click": (e: RowClickEventArgs) => true,
  },
  setup(props, { slots, emit }) {
    const renderColumns = computed<VTableColumnsType>(() => {
      if (props.columns) return props.columns;
      const columns: VTableColumnsType = [];

      const children: VNode[] = slots.default?.() || [];
      for (const child of children) {
        if (child?.type && typeof child.type === "object" && (child.type as ComponentOptions).name === "v-table-column") {
          columns.push(getColumnItem(child));
        }

        if (Array.isArray(child.children) && child.children.length > 0) {
          // 查找下一层子节点中的列
          for (const subChild of child.children) {
            const subVNode = subChild as VNode | undefined;
            if (subVNode?.type && typeof subVNode.type === "object" && (subVNode.type as ComponentOptions).name === "v-table-column") {
              columns.push(getColumnItem(subVNode));
            }
          }
        }
      }

      return columns;
    });

    const virtListRef = shallowRef<VirtListReturn<any>>();
    watch(
      () => props.dataSource,
      () => {
        nextTick(() => {
          if (virtListRef.value) {
            virtListRef.value.scrollToTop();
            virtListRef.value.forceUpdate();
          }

        }).catch(noop);
      },
      { deep: props.deepWatch },
    );

    const onCellClick = (e: MouseEvent) => {
      const target = e.target as HTMLElement;
      const id = target.dataset.id;
      if (id) {
        const record = props.dataSource?.find(item => item[props.rowKey] === id);
        if (record) {
          emit("row-click", { record, index: +target.dataset.rowIndex! });
        }
      }
    };

    useRender(() => (
      <div class="v-table" style={`--row-height: ${props.rowHeight}px`}>
        <VirtList
          ref={virtListRef}
          itemKey={props.rowKey}
          list={props.dataSource}
          minSize={props.rowHeight}
          class="v-table-list"
          itemClass="v-table-row"
        >
          {{
            stickyHeader: () => (
              <div class="v-table-row v-table-header" style={{ height: props.rowHeight + "px" }}>
                {renderColumns.value.map((col, i) => (
                  <div
                    key={col.key ?? i}
                    class="v-table-cell"
                    style={{
                      maxWidth: typeof col.width === "number" ? `${col.width}px` : col.width,
                      flexBasis: typeof col.width === "number" ? `${col.width}px` : col.width,
                      flexGrow: col.width ? 0 : undefined,
                      justifyContent: col.align,
                    }}
                  >
                    {col.title}
                  </div>
                ))}
              </div>
            ),
            default: ({ itemData: record, index }: any) => {
              return renderColumns.value.map((col, i) => {
                const value = col.dataIndex && record[col.dataIndex];
                return (
                  <div
                    key={col.key ?? i}
                    class="v-table-cell"
                    data-id={record[props.rowKey]}
                    data-row-index={index}
                    data-cell-index={i}
                    onClick={onCellClick}
                    style={{
                      maxWidth: typeof col.width === "number" ? `${col.width}px` : col.width,
                      flexBasis: typeof col.width === "number" ? `${col.width}px` : col.width,
                      flexGrow: col.width ? 0 : undefined,
                      justifyContent: col.align,
                    }}
                  >
                    {col.render ? col.render({ index, record, value } as VTableColumnRenderArgs) : value}
                  </div>
                );
              });
            },
            empty: () => (
              <Fragment>
                {!props.loading && (!props.dataSource || props.dataSource.length === 0) && (
                  <div class="v-table-empty">
                    <Empty description="空空如也" />
                  </div>
                )}
              </Fragment>
            ),
          }}
        </VirtList>

        {props.loading && (
          <div class="abs-fill flex-center">
            <Spin spinning tip="数据加载中..." />
          </div>
        )}
      </div>
    ));

    return {
      renderColumns,
    };
  },
});
